#include "stdafx.h"
#include "text.h"
#include "texture.h"

//
// DevIL headers to load the font texture...
#include <IL/il.h>
#include <IL/ilu.h>
#include <IL/ilut.h>

static float white[4] = {1.0, 1.0, 1.0, 1.0};
static float gray[4] = {0.5, 0.5, 0.5, 1.0};

float tPoints[257][2];

int GLText::fontBlockCol = 16;
int GLText::fontBlockRow = 16;
int GLText::fontGetModes = 1; 
int GLText::fontRestoreModes = 2; 
const int GLText::fontMaxLen = 1024;
int GLText::fontTabSpace = 4;
float GLText::fontITOF = 1.f/255.f;
int GLText::fontItalic = 8;

GLText::GLText()
{
	ColorCopy(fgColor, white); 
	ColorCopy(gdColor, gray);
	ColorCopy(gdColor, gray);
	size = 12;
	shadow = 0;
	gradient = 0;
	italic = 0;
	bold = 0;
	region = 0;;         
	regionX = 0;
	regionY = 0;
	regionW = 0;
	regionH = 0;
	tIncX = 0.0f;
	tIncY = 0.0f;
	blockRow = fontBlockRow;
	blockCol = fontBlockCol;
	texId = 0;
}

GLText::~GLText()
{
	if(glIsTexture(texId))
		glDeleteTextures(1,&texId);
}

void GLText::Reset()
{
	size = 12;
	shadow = 0;
	region = 0;
	gradient = 0;
	italic = 0;
	bold = 0;
	regionX = 0;
	regionY = 0;
	regionW = 0;
	regionH = 0;
	ForeColorReset();
	ShadowColorReset();
	GradientColorReset();
}

int GLText::Load(char *name)
{
	//GLuint TexID = 0;			// GL texture ID
	//ILuint ImgId = 0;			// IL image ID

	//ilGenImages(1, &ImgId);		// Generate the main image name to use.
	//ilBindImage(ImgId);			// Bind this image name.

	//// Loads the image specified by filename into the ImgId image.
	//if (!ilLoadImage(name))
	//{
	//	Log("LoadTexture(): no image %s found\n",name);
	//	return 0;
	//}

	//int width = ilGetInteger(IL_IMAGE_WIDTH);
	//int height = ilGetInteger(IL_IMAGE_HEIGHT);

	//glGenTextures(1, &TexID);
	//glBindTexture (GL_TEXTURE_2D, TexID);
	//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	//glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	//glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	//glTexImage2D (GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, ilGetData());
	////glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, ilGetData());

	//// We're done with our image, so we go ahead and delete it.
	//ilDeleteImages(1, &ImgId);


	//texId = TexID;
	//texId = LoadTexture("data/font.tga");
	texId = LoadFont(name);
	blockRow = fontBlockRow;
	blockCol = fontBlockCol;
	MakeMap();

	// finally return the resulting texture id
	return texId;
}

int GLText::LoadEx(char *name, int row, int col)
{
	//.... nothing for now
	return 0;
}

void GLText::Init(GLuint texture)
{
	// clear the previous loaded texture......
	if(glIsTexture(texId))
		glDeleteTextures(1,&texId);
	texId = texture;
	blockRow = fontBlockRow;
	blockCol = fontBlockCol;
	MakeMap();
}

void GLText::Init(GLuint texture,int row, int col)
{
	// make sure we have valid dimensions
	if (row * col != 256)
		return;

	if(glIsTexture(texId))
		glDeleteTextures(1,&texId);
	texId = texture;
	blockRow = row;
	blockCol = col;
	MakeMap();
}

void GLText::DrawChar(char c, int x, int y, int size, int shadow)
{
	if (!gradient && !shadow)
		glColor4fv(fgColor);
	else if (!gradient && shadow)
		glColor4fv(bgColor);
	else if (gradient && !shadow)
		glColor4fv(gdColor);

	glBegin (GL_QUADS);
	{
		glTexCoord2f(tPoints[(int) c][0], tPoints[(int) c][1]);
		glVertex2i(x, y);

		glTexCoord2f(tPoints[(int) c][0] + tIncX, tPoints[(int) c][1]);
		glVertex2i(x + size, y);

		if (!shadow)
			glColor4fv(fgColor);
		else 
			glColor4fv(bgColor);

		glTexCoord2f(tPoints[(int) c][0] + tIncX, tPoints[(int) c][1] + tIncY);
		glVertex2i(x + size + italic, y + size);

		glTexCoord2f(tPoints[(int) c][0], tPoints[(int) c][1] + tIncY);
		glVertex2i(x + italic, y + size);
	}
	glEnd ();
}

void GLText::RenderChar(char c, int x, int y, int size)
{
	if (shadow)
	{
		if (!bold)
			DrawChar(c, x + 1,  y + 1, size, 1);
		else DrawChar(c, x + 2,  y + 1, size, 1);
	}

	DrawChar(c, x, y, size, 0);

	if (bold)
		DrawChar(c, x + 1, y, size, 0);
}

void GLText::MakeMap()
{
#define vCopy(d,x,y) {d[0] = x; d[1] = y;}
	int i = 0;
	float x, y;

	//tIncX = (float)pow (blockCol, -1);
	//tIncY = (float)pow (blockRow, -1);
	tIncX = 1.f/(float)blockCol;
	tIncY = 1.f/(float)blockRow;


	for (y = 1 - tIncY; y >= 0; y -= tIncY)
		for (x = 0; x <= 1 - tIncX; x += tIncX, i ++)
			vCopy(tPoints[i], x, y);
#undef vCopy
	//printf("end of MakeMap()\n");
}

void GLText::SetModes(int state)
{
	static int matrixMode;
	static int polyMode[2];
	static int lightingOn;
	static int blendOn;
	static int depthOn;
	static int textureOn;
	static int scissorOn;
	static int blendSrc;
	static int blendDst;
	//static int alphaTest;

	// grab the modes that we might need to change
	if (state==fontGetModes)
	{
		glGetIntegerv(GL_POLYGON_MODE, polyMode);
		if (polyMode[0] != GL_FILL)
			glPolygonMode(GL_FRONT, GL_FILL);
		if (polyMode[1] != GL_FILL)
			glPolygonMode(GL_BACK, GL_FILL);

		textureOn = glIsEnabled(GL_TEXTURE_2D);
		if (!textureOn)
			glEnable(GL_TEXTURE_2D);

		depthOn = glIsEnabled(GL_DEPTH_TEST);
		if (depthOn)
			glDisable(GL_DEPTH_TEST);

		lightingOn = glIsEnabled(GL_LIGHTING);
		if (lightingOn) 
			glDisable(GL_LIGHTING);

		scissorOn= glIsEnabled(GL_SCISSOR_TEST);
		if (!scissorOn) 
			glEnable(GL_SCISSOR_TEST);

		glGetIntegerv(GL_MATRIX_MODE, &matrixMode); 

		blendOn= glIsEnabled(GL_BLEND);        
		glGetIntegerv(GL_BLEND_SRC, &blendSrc);
		glGetIntegerv(GL_BLEND_DST, &blendDst);

		if (!blendOn)
			glEnable(GL_BLEND);

		/*alphaTest = glIsEnabled(GL_ALPHA_TEST);
		if(!alphaTest)
			glEnable(GL_ALPHA_TEST);*/

		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		// texture stuff
		glPushAttrib(GL_TEXTURE_BIT);
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	}
	else if (state==fontRestoreModes)
	{
		// put everything back where it was before
		if (polyMode[0] != GL_FILL)
			glPolygonMode(GL_FRONT, polyMode[0]);
		if (polyMode[1] != GL_FILL)
			glPolygonMode(GL_BACK, polyMode[1]);

		if (lightingOn)
			glEnable(GL_LIGHTING);

		if (!blendOn)
		{
			glDisable(GL_BLEND);
			glBlendFunc(blendSrc, blendDst);
		}
		else glBlendFunc(blendSrc, blendDst);

		if (depthOn)
			glEnable(GL_DEPTH_TEST);

		if (!textureOn)
			glDisable(GL_TEXTURE_2D);

		if (!scissorOn) 
			glDisable(GL_SCISSOR_TEST);

		/*if(!alphaTest)
			glDisable(GL_ALPHA_TEST);*/

		glMatrixMode(matrixMode);

		glPopAttrib();
	}
}

/*
*	how many times the character f is encountered in string s
*/
int GLText::GetCharHits(char *s, char f)
{
	int hits;
	for (hits = 0; *s; *s++)
		if (*s == f)
			hits ++;

	return hits;
}

int GLText::BoldMode(char *s)
{
	s++;
	if (*s=='+')
		bold=1;
	else if(*s=='-')
		bold=0;
	else return -1;
	return 2;
}

int GLText::ItalicsMode(char *s)
{
	s++;
	if(*s=='+')
		italic=fontItalic;
	else if(*s=='-')
		italic=0;
	else 
		return -1;
	return 2;
}

int GLText::SetColorFromToken(char *s)
{
	int clr[4];
	int ret = 1;

	if (*s=='c')
	{
		s++;
		if(sscanf(s,"(%d %d %d)",&clr[0], &clr[1], &clr[2])!=3)
			return -1;
		Color3f(clr[0]*fontITOF,clr[1] * fontITOF, clr[2] * fontITOF);
	}
	else if (*s=='a')
	{
		s++;
		if (sscanf(s, "(%d %d %d %d)", &clr[0], &clr[1], &clr[2], &clr[3]) != 4)
			return -1;
		Color4f(clr[0] * fontITOF, clr[1] * fontITOF, clr[2] * fontITOF, clr[3] * fontITOF);
	}

	while (*s != ')' && ret ++)
		s += 1;

	return ret + 1;
}

void GLText::Draw(int xpos, int ypos, char *s, ...)
{
	va_list	msg;
	char buffer[fontMaxLen] = {'\0'};
	int vPort[4];

	va_start (msg, s);
#ifdef _WIN32
	_vsntprintf (buffer, fontMaxLen - 1, s, msg);	
#else /* linux */
	vsnprintf (buffer, fontMaxLen - 1, s, msg);	
#endif
	va_end (msg);

	//
	// get current viewport
	glGetIntegerv(GL_VIEWPORT, vPort);
	// setup various OpenGL things that we need
	SetModes(fontGetModes);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	glOrtho(0, vPort[2], 0, vPort[3], -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glBindTexture(GL_TEXTURE_2D, texId);

	//
	// draw the string
	WalkString(buffer, xpos, ypos, vPort);

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();   
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	SetModes (fontRestoreModes);
	Reset ();
}

void GLText::WalkString(char *buffPtr, int xpos, int ypos, int *vPort)
{
	int x = xpos;
	int y = ypos;
	int carrage = 0;
	int tabs = 0;
	int len = (int)strlen(buffPtr);
	int xMax;

	xMax = vPort[0] + vPort[2];

	carrage = GetCharHits(buffPtr, '\n');
	tabs = GetCharHits(buffPtr, '\t');

	if (!tabs)
		tabs = 1;
	else 
		tabs *= fontTabSpace;

	if (region)
	{
		ScissorTextRegion();
		x = regionX;
		y = (regionY + regionH) - size;
	} else 
		ScissorNormal(xpos, ypos, tabs, carrage, size, len);

	//
	// draw
	for ( ; *buffPtr; *buffPtr++, x += size)
	{
		if (x > xMax)
			break;

		if (region)
		{
			if (x + size > (regionX + regionW))
			{
				y -= size;
				x = regionX;
			}
			if (y < regionY)
				break;
		}

		switch (*buffPtr)
		{
		case '\n':
			y -= size;
			x = xpos - size;
			continue; 
			break;

		case '\t':
			x += (size * fontTabSpace);
			continue; 
			break;

		case '\\':
			buffPtr += SlashParser(buffPtr, &x, &y);
			if (*buffPtr == '\n' || *buffPtr == '\t')
			{
				buffPtr -= 1;
				continue;
			}
			break;
		default:
			RenderChar(*buffPtr, x, y, size);
			break;
		}
	}
}

int GLText::SlashParser(char *buffPtr, int *x, int *y)
{
	int ret = 0;

	*buffPtr++;

	if (!*buffPtr)
		return ret;

	switch (*buffPtr)
	{
	case 'a':
	case 'c':
		*x -= size;
		return SetColorFromToken(buffPtr);
		break;
	case 'i':
		*x -= size;
		return ItalicsMode(buffPtr);
		break;
	case 'b':
		*x -= size;
		return BoldMode(buffPtr);
		break;
	default:
		*buffPtr--;
		RenderChar(*buffPtr, *x, *y, size);
		return ret;
		break;
	}
}

void GLText::ScissorNormal(int xpos, int ypos, int tabs, int carrage, int size, int len)
{
	int sy, ex, ey;

	ex = len * size * tabs;

	if (carrage)
	{
		sy = ypos - (size * carrage);
		ey = size * (carrage + 1);
	} 
	else
	{
		sy = ypos;
		ey = size;
	}

	glScissor(xpos, sy, ex, ey);
}

